package core;

import static org.junit.Assert.*;

import java.util.Iterator;
import java.util.TreeSet;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import persistence.DataPersistor;

import core.Container;
import core.ItemManager;
import core.ProductManager;
import core.RootStorageUnit;
import core.exception.IllegalAddException;
import core.exception.IllegalRemoveException;
import core.exception.NotFoundException;
import core.exception.SaveException;


public class ContainerTest {
	// The container test starts by creating this tree:
	//	RootStorageUnit
	//SU1				SU2				SU3			SU4		SU5
	//PG1-1   PG1-2    PG2-1   PG2-2	    PG3-1		PG4-1
	//cPG1-2   cPG2-1							cPG4-1
	//ccPG1-2
	//
	//Where Product A is contained in PG1-2, cPG2-1, and SU3
	//with three, two, and one items in each respectively.
	private Container _root;
	private Container _su1;
	private Container _su2;
	private Container _su3;
	private Container _su4;
	private Container _su5;
	private Container _pg1_1;
	private Container _pg1_2;
	private Container _cpg1_2;
	private Container _ccpg1_2;
	private Container _pg2_1;
	private Container _pg2_2;
	private Container _cpg2_1;
	private Container _pg3_1;
	private Container _pg4_1;
	private Container _cpg4_1;
	private Product _p;
	private Product _p2;
	private Product _p3;
	
	private String[] names = {"SU1","PG1-1","PG1-2","cPG1-2",
			"ccPG1-2","SU2","PG2-1","PG2-2","cPG2-1","SU3","PG3-1","SU4","PG4-1",
			"cPG4-1","SU5","RootStorageUnit"
	};
	
	private Item[] _pItems;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		DataPersistor.instance().init("-serial");
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
		// Reset root storage unit
		RootStorageUnit.setInstance(null);
		ProductManager.setInstance(null);
		ItemManager.setInstance(null);
		
		_root = RootStorageUnit.instance();
		
		_su1 = _root.addContainer(names[0]);
		_pg1_1 = _su1.addContainer(names[1]);
		_pg1_2 = _su1.addContainer(names[2]);
		_cpg1_2 = _pg1_2.addContainer(names[3]);
		_ccpg1_2 = _cpg1_2.addContainer(names[4]);
		
		_su2 = _root.addContainer(names[5]);
		_pg2_1 = _su2.addContainer(names[6]);
		_pg2_2 = _su2.addContainer(names[7]);
		_cpg2_1 = _pg2_1.addContainer(names[8]);
		
		_su3 = _root.addContainer(names[9]);
		_pg3_1 = _su3.addContainer(names[10]);
		
		_su4 = _root.addContainer(names[11]);
		_pg4_1 = _su4.addContainer(names[12]);
		_cpg4_1 = _pg4_1.addContainer(names[13]);
		
		_su5 = _root.addContainer(names[14]);

		_p = new Product(new Barcode("111111111111", BarcodeType.PRODUCT), 
				"my product", new Amount(1,UnitOfMeasurement.count),30,0);
		_p2 = new Product(new Barcode("222222222222",BarcodeType.PRODUCT), "my p2",
				new Amount (2, UnitOfMeasurement.count),30,0);
		_p3 = new Product(new Barcode("232222222222",BarcodeType.PRODUCT), "my p2",
				new Amount (3, UnitOfMeasurement.count),30,0);		
		
		ProductManager.instance().addProduct(_p);
		ProductManager.instance().addProduct(_p2);
		ProductManager.instance().addProduct(_p3);
		
		ProductFacade.instance().addProductToContainer(_p, _pg1_2);
		ProductFacade.instance().addProductToContainer(_p, _cpg2_1);
		ProductFacade.instance().addProductToContainer(_p, _su3);
		
		// _cpg1_2 set up to have once held stuff, but to now be empty.
		ProductFacade.instance().addProductToContainer(_p2, _cpg1_2);
		Item i = ItemManager.instance().createItem(_p2, _cpg1_2);
		ItemManager.instance().removeItem(i);
		
		ProductFacade.instance().addProductToContainer(_p3, _ccpg1_2);
		Item i2 = ItemManager.instance().createItem(_p3, _ccpg1_2);
		ItemManager.instance().removeItem(i2);
		
		ProductFacade.instance().addProductToContainer(_p3, _pg4_1);
		Item i3 = ItemManager.instance().createItem(_p3, _pg4_1);
		ItemManager.instance().removeItem(i3);
		
		_pItems = new Item[6];
		
		_pItems[0] = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_pg1_2, _pItems[0]);
		_pItems[1] = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_pg1_2, _pItems[1]);
		_pItems[2] = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_pg1_2, _pItems[2]);
		
		_pItems[3] = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_cpg2_1, _pItems[3]);
		_pItems[4] = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_cpg2_1, _pItems[4]);
		
		_pItems[5] = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_su3, _pItems[5]);
	}

	@After
	public void tearDown() throws Exception {
		ProductManager.setInstance(null);
		RootStorageUnit.setInstance(null);
	}

	@Test
	public void testGetName() {
		// check everything in the initial tree to make sure it has the right name.
		assertEquals(_root.getName(),names[15]);
		assertEquals(_su1.getName(), names[0]);
		assertEquals(_pg1_1.getName(), names[1]);
		assertEquals(_pg1_2.getName(), names[2]);
		assertEquals(_cpg1_2.getName(), names[3]);
		assertEquals(_ccpg1_2.getName(), names[4]);
		assertEquals(_su2.getName(), names[5]);
		assertEquals(_pg2_1.getName(), names[6]);
		assertEquals(_pg2_2.getName(), names[7]);
		assertEquals(_cpg2_1.getName(), names[8]);
		assertEquals(_su3.getName(), names[9]);
		assertEquals(_pg3_1.getName(), names[10]);
		assertEquals(_su4.getName(), names[11]);
		assertEquals(_pg4_1.getName(), names[12]);
		assertEquals(_cpg4_1.getName(), names[13]);
		assertEquals(_su5.getName(), names[14]);
	}

	@Test
	public void testCanAddContainer() {
		// canAddContainer only cares about names
		assertTrue("Should be able to add unique name",_root.canAddContainer("garbagecan"));
		assertFalse("Should not add, name not unique",_root.canAddContainer("SU1"));
		assertTrue("Names are case sensitive",_root.canAddContainer("su1"));	
		
		assertTrue("Storage unit, should add unique name",_su1.canAddContainer("garbagecan"));
		assertTrue("Storage unit should not care about names of descendants",_su1.canAddContainer("cPG1-1"));
		assertFalse("Storage unit should not add, name not unique",_su1.canAddContainer("PG1-1"));
	
		assertFalse("ProductGroup should not add, name not unique",_pg4_1.canAddContainer("cPG4-1"));
		assertTrue("Product Group should add unique name",_pg4_1.canAddContainer("garbagecan"));
		
		assertFalse("cannot add null name",_su4.canAddContainer(null));
		assertFalse("cannot add empty name",_su5.canAddContainer(""));
	}

	@Test
	public void testAddContainer() {
		// Verify integrity of setup (containers that we thought added actually are)
		// It should be sufficient to completely test root, su1, and all of su1's children
		// as all the other storage units have simpler trees and are mainly in setup to
		// test moving and transferring products.
		
		// Does root storage unit have all proper pointers after the adds
		assertNull("Root Storage unit never has parent",_root._parent);
		assertNull("Root storage unit never has a storage unit parent",_root._storageUnit);
		
		assertTrue("Root storage unit has su1 child",_root._children.containsKey(names[0]));
		assertEquals("Root storage unit has su1 child",_su1, _root._children.get(names[0]));
		
		assertTrue("Root storage unit has su2 child",_root._children.containsKey(names[5]));
		assertEquals("Root storage unit has su2 child",_su2, _root._children.get(names[5]));
		
		assertTrue("Root storage unit has su3 child",_root._children.containsKey(names[9]));
		assertEquals("Root storage unit has su3 child",_su3, _root._children.get(names[9]));
		
		assertTrue("Root storage unit has su4 child",_root._children.containsKey(names[11]));
		assertEquals("Root storage unit has su4 child",_su4, _root._children.get(names[11]));
		
		assertTrue("Root storage unit has su5 child",_root._children.containsKey(names[14]));
		assertEquals("Root storage unit has su5 child",_su5, _root._children.get(names[14]));
		
		assertEquals("Root storage unit child size",5, _root._children.size());
		
		// Does SU1 have all proper pointers
		assertEquals("SU1 parent",_root,_su1._parent);
		assertEquals("SU1 su",_su1,_su1._storageUnit);
		
		assertTrue("SU1 has child pg1-1",_su1._children.containsKey(names[1]));
		assertEquals("SUQ has child pg1-1",_pg1_1,_su1._children.get(names[1]));
		
		assertTrue("SU1 has child pg1-2",_su1._children.containsKey(names[2]));
		assertEquals("SU1 has child pg1-2",_pg1_2,_su1._children.get(names[2]));
		
		assertEquals("SU1 child size",2,_su1._children.size());
		
		TreeSet<Container> su1ExpectedDescendants = new TreeSet<Container>();
		su1ExpectedDescendants.add(_su1);
		su1ExpectedDescendants.add(_pg1_1);
		su1ExpectedDescendants.add(_pg1_2);
		su1ExpectedDescendants.add(_cpg1_2);
		su1ExpectedDescendants.add(_ccpg1_2);
		assertEquals("SU1 descendants",su1ExpectedDescendants, _su1._storageUnit.getDescendants());
		
		// Does PG1-1 have all proper pointers
		assertEquals("PG1-1 parent",_su1,_pg1_1._parent);
		assertEquals("PG1-1 storage unit",_su1,_pg1_1._parent);
		
		assertTrue("PG1-1 children empty",_pg1_1._children.isEmpty());
		
		// Does PG1-2 have all proper pointers
		assertEquals("PG1-2 parent",_su1,_pg1_2._parent);
		assertEquals("PG1-2 storage unit",_su1,_pg1_2._storageUnit);
		
		assertTrue("PG1-2's child", _pg1_2._children.containsKey(names[3]));
		assertEquals("PG1-2's child",_cpg1_2, _pg1_2._children.get(names[3]));
		assertEquals("PG1-2's children size",1,_pg1_2._children.size());
		
		// Does cPG1-2 have proper pointers
		assertEquals("cPG1-2 parent",_pg1_2,_cpg1_2._parent);
		assertEquals("cpg1-2 storage unit",_su1,_cpg1_2._storageUnit);
		
		assertTrue("cpg1-2's child",_cpg1_2._children.containsKey(names[4]));
		assertEquals("cpg1-2's child",_ccpg1_2,_cpg1_2._children.get(names[4]));
		assertEquals("cpg1-2's child size",1,_cpg1_2._children.size());
		
		// Does ccPG1 have proper pointers
		assertEquals("ccPG1's parent",_cpg1_2,_ccpg1_2._parent);
		assertEquals("ccPG1's storage unit",_su1, _ccpg1_2._storageUnit);
		assertTrue("ccPG1 has no kids",_ccpg1_2._children.isEmpty());
		
	}
	
	@Test(expected=IllegalAddException.class)
	public void testAddContainerException1() throws IllegalAddException{
		// test for can add container fails on null
		_su1.addContainer(null);
	}
	
	@Test(expected=IllegalAddException.class)
	public void testAddContainerException2() throws IllegalAddException{
		// test for can add container fails on empty string
		_su1.addContainer("");
	}
	
	@Test(expected=IllegalAddException.class)
	public void testAddContainerException3() throws IllegalAddException{
		// test for can add container fails on already exists
		_su1.addContainer(names[1]);
	}	

	@Test
	public void testCanRemoveContainer() {
		assertFalse("Child has items",_root.canRemoveContainer(names[0]));
		assertTrue(_su1.canRemoveContainer(names[1]));
		assertFalse("Container has items",_su1.canRemoveContainer(names[2]));
		assertTrue(_pg1_2.canRemoveContainer(names[3]));
		assertTrue(_cpg1_2.canRemoveContainer(names[4]));
		
		assertFalse("Child has items", _root.canRemoveContainer(names[5]));
		assertFalse("Child has items",_su2.canRemoveContainer(names[6]));
		assertTrue(_su2.canRemoveContainer(names[7]));
		assertFalse("Container has items",_pg2_1.canRemoveContainer(names[8]));
		
		assertFalse("Container has items", _root.canRemoveContainer(names[9]));
		assertTrue(_su3.canRemoveContainer(names[10]));
		
		assertTrue(_root.canRemoveContainer(names[11]));
		assertTrue(_su4.canRemoveContainer(names[12]));
		assertTrue(_pg4_1.canRemoveContainer(names[13]));
		
		assertTrue(_root.canRemoveContainer(names[14]));


	}

	@Test
	public void testRemoveContainer() throws IllegalRemoveException, SaveException {
		// Test remove PG child with no children (PG1-1)
		_su1.removeContainer(names[1]);
		
		assertFalse("Parent should not contain that child",_su1.containsContainer(names[1]));
		assertFalse("Parent should not contain that child",_su1.containsContainer(_pg1_1));
		assertFalse("Parent data should not contain child",_su1._children.containsKey(names[1]));
		assertFalse("Storage Unit descendents should not contain child",
				_su1._storageUnit.getDescendants().contains(_pg1_1));
		
		
		// Test remove PG with children
		_pg1_2.removeContainer(names[3]);
		
		assertFalse("Parent should not contain that child",_pg1_2.containsContainer(_cpg1_2));
		assertFalse("Parent should not contain taht child",_pg1_2.containsContainer(names[3]));
		assertFalse("Parent data should not contain child",_pg1_2._children.containsKey(names[1]));
		assertFalse("Storage Unit descendents should not contain child",
				_su1._storageUnit.getDescendants().contains(_cpg1_2));
		assertFalse("Storage Unit descendants should not contain child's child",
				_su1._storageUnit.getDescendants().contains(_ccpg1_2));
		
		try {
			ProductManager.instance().getProductsForContainer(_cpg1_2);
			fail("Should have got exception!");
		} catch (NotFoundException e) {}
		try {
			ProductManager.instance().getProductsForContainer(_ccpg1_2);
			fail("Should have got exception!");
		} catch (NotFoundException e) {}
		
		assertNull("Item Manager should not remember child",
				ItemManager.instance().itemsInContainer(_cpg1_2));
		assertNull("Item Manager should not remember child's child",
				ItemManager.instance().itemsInContainer(_ccpg1_2));
		
		// test remove storage unit without children
		_root.removeContainer(names[14]);
		
		assertFalse("Parent should not remember child",_root.containsContainer(_su5));
		assertFalse("Parent should not remember child",_root.containsContainer(names[14]));
		assertFalse("Parent should not remember child",_root._children.containsKey(names[14]));
		
		// test remove storage unit with children
		_root.removeContainer(names[11]);
		assertFalse("Parent should not remember child",_root.containsContainer(_su4));
		assertFalse("Parent should not remember child",_root.containsContainer(names[11]));
		assertFalse("Parent should not remember child",_root._children.containsKey(names[11]));
				
		try {
			ProductManager.instance().getProductsForContainer(_pg4_1);
			fail("Should have caught exception here!");
		} catch (NotFoundException e) {}
		
		assertNull("Item Manager should not remember descendants",
				ItemManager.instance().itemsInContainer(_pg4_1));

	}

	@Test
	public void testCanSetChildContainerName() {
		assertFalse("Can't set name to null",_su1.canSetChildContainerName(names[1], null));
		assertFalse("Can't set name to empty string",_su1.canSetChildContainerName(names[1], ""));
		assertFalse("Can't set name to something that already exists",_su1.canSetChildContainerName(names[1], names[2]));
		assertFalse("Can't set name on nonexistant child",_su1.canSetChildContainerName("dne", "garbagecan"));
		
		assertTrue("Should be able to change to valid name",_su1.canSetChildContainerName(names[1], "garbagecan"));
	}

	@Test
	public void testSetChildContainerName() {
		_su1.setChildContainerName(names[1], "garbagecan");
		assertEquals("Name should change","garbagecan",_pg1_1._name);
		assertTrue("SU1 should have child of new name",_su1._children.containsKey("garbagecan"));
		assertFalse("SU1 should not have old name",_su1._children.containsKey(names[1]));
	}
	
	@Test(expected=IllegalArgumentException.class)
	public void testSetChildContainerNameException1(){
		_su1.setChildContainerName(names[1], null);
	}
	
	@Test(expected=IllegalArgumentException.class)
	public void testSetChildContainerNameException2(){
		_su1.setChildContainerName(names[1], "");
	}
	
	@Test(expected=IllegalArgumentException.class)
	public void testSetChildContainerNameException3(){
		_su1.setChildContainerName(names[1], names[2]);
	}
	
	@Test(expected=IllegalArgumentException.class)
	public void testSetChildContainerNameException4(){
		_su1.setChildContainerName("dne", "garbagecan");
	}	

	@Test
	public void testGetChildContainers() {
		// Test RootStorageUnit
		TreeSet<Container> expected = new TreeSet<Container>();
		expected.add(_su1);
		expected.add(_su2);
		expected.add(_su3);
		expected.add(_su4);
		expected.add(_su5);
		
		Iterator<Container> iter = _root.getChildContainers();
		while (iter.hasNext()){
			Container child = iter.next();
			assertTrue("Child it should have",expected.contains(child));
			expected.remove(child);
		}
		assertTrue("Should have found all children",expected.isEmpty());
		
		Iterator<Container> iter2 = _pg1_1.getChildContainers();
		assertFalse("No children",iter2.hasNext());
	}

	@Test
	public void testContainsContainerString() {
		assertTrue(_root.containsContainer(names[0]));
		assertTrue(_root.containsContainer(names[5]));
		assertTrue(_root.containsContainer(names[9]));
		assertTrue(_root.containsContainer(names[11]));
		assertTrue(_root.containsContainer(names[14]));
		
		assertFalse(_root.containsContainer("dne"));
		assertFalse(_root.containsContainer(""));
		assertFalse(_root.containsContainer((String)null));
		assertFalse("capitalization matters",_root.containsContainer("su1"));
		assertFalse("only look at direct children",_root.containsContainer("PG1-1"));
	}

	@Test
	public void testContainsContainerContainer() {
		assertTrue(_root.containsContainer(_su1));
		assertTrue(_root.containsContainer(_su2));
		assertTrue(_root.containsContainer(_su3));
		assertTrue(_root.containsContainer(_su4));
		assertTrue(_root.containsContainer(_su5));
		
		assertFalse(_root.containsContainer((Container) null));
		assertFalse("only look at direct children",_root.containsContainer(_pg1_1));
	}

	@Test
	public void testLookupContainer() {
		assertEquals(_su1,_root.lookupContainer(names[0]));
		assertEquals(_su2,_root.lookupContainer(names[5]));
		assertEquals(_su3,_root.lookupContainer(names[9]));
		assertEquals(_su4,_root.lookupContainer(names[11]));
		assertEquals(_su5,_root.lookupContainer(names[14]));
		
		assertNull(_root.lookupContainer(null));
		assertNull(_root.lookupContainer("dne"));
	}

	@Test
	public void testGetParent() {
		// Proper parents are check in testAddContainer. Here just check that _parent
		// that we were checking in testAddContainer matches what getParent returns.
		assertEquals(_su1._parent, _su1.getParent());
		assertEquals(_su2._parent, _su2.getParent());
		assertEquals(_su3._parent, _su3.getParent());
		assertEquals(_su4._parent, _su4.getParent());
		assertEquals(_su5._parent, _su5.getParent());
		assertNull(_root.getParent());
	}

	@Test
	public void testGetStorageUnit() {
		assertEquals(_su1._storageUnit, _su1.getStorageUnit());
		assertEquals(_su1,_su1.getStorageUnit());
		
		assertEquals(_pg1_1._storageUnit, _pg1_1.getStorageUnit());
		assertEquals(_su1,_pg1_1.getStorageUnit());
	}

	@Test
	public void testAddProduct() throws IllegalAddException {
		// add to different container within storage unit
		ProductManager.instance().addProductToContainer(_p, _pg1_1);
		
		assertTrue("Item should come with product",_pg1_1.containsItem(_pItems[0]));
		assertTrue("Item should come with product",_pg1_1.containsItem(_pItems[1]));
		assertTrue("Item should come with product",_pg1_1.containsItem(_pItems[2]));
		assertTrue("Should get product",_pg1_1.containsProduct(_p));
		
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[0]));
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[1]));
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[2]));
		assertFalse("Should have lost product",_pg1_2.containsProduct(_p));		
		
		// add to a storage unit where it isn't there before
		ProductManager.instance().addProductToContainer(_p, _su4);
		
		assertTrue("Should have product",_su4.containsProduct(_p));
	}

	@Test
	public void testCanRemoveProduct() {
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _su1));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _pg1_1));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _pg1_2));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _cpg1_2));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _ccpg1_2));
		
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _su2));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _pg2_1));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _cpg2_1));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _pg2_2));
		
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _su3));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _pg3_1));
		
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _su4));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _pg4_1));
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _cpg4_1));
		
		assertFalse(ProductManager.instance().canRemoveProductFromContainer(_p, _su5));
		
		assertTrue(ProductManager.instance().canRemoveProductFromContainer(_p2, _cpg1_2));
		assertTrue(ProductManager.instance().canRemoveProductFromContainer(_p3, _ccpg1_2));
		assertTrue(ProductManager.instance().canRemoveProductFromContainer(_p3, _pg4_1));
	}

	@Test
	public void testRemoveProduct() throws IllegalRemoveException {
		ProductManager.instance().removeProductFromContainer(_p2, _cpg1_2);
		assertFalse(_cpg1_2.containsProduct(_p2));
	}
	
	@Test
	public void testGetProducts() throws IllegalRemoveException {
		assertTrue(_cpg1_2.getProducts().hasNext());
		
		ProductManager.instance().removeProductFromContainer(_p2, _cpg1_2);
		assertFalse(_cpg1_2.getProducts().hasNext());
		
		assertNull(_su1.getProducts());
		
	}

	@Test
	public void testContainsProduct() {
		assertTrue(_pg1_2.containsProduct(_p));
		assertFalse(_su1.containsProduct(_p));
		assertFalse(_su1.containsProduct(null));
	}

	@Test
	public void testAddItem() throws IllegalAddException {
		Item i = ItemManager.instance().createItem(_p, _su1);
		ItemManager.instance().addItemToContainer(_su1, i);
		
		assertFalse(_su1.containsItem(i));
		assertTrue(_pg1_2.containsItem(i));
		
		Item i2 = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_su2, i2);
		
		assertTrue(_cpg2_1.containsItem(i2));
		assertFalse(_su2.containsItem(i2));
		
		Item i3 = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_su3, i3);
		
		assertTrue(_su3.containsItem(i3));
		
		Item i4 = ItemManager.instance().createItem(_p, null);
		ItemManager.instance().addItemToContainer(_su4, i4);
		
		assertTrue(_su4.containsItem(i4));
		assertTrue(_su4.containsProduct(_p));
	}

	@Test
	public void testTransferItem() throws IllegalAddException {
		ItemManager.instance().transferItem(_su2, _pItems[0]);
		
		assertFalse("SU2 should not end up with items",_su2.containsItem(_pItems[0]));
		assertFalse("SU2 should not end up with product",_su2.containsProduct(_p));
		
		assertTrue(_cpg2_1.containsItem(_pItems[0]));
		assertTrue(_cpg2_1.containsProduct(_p));
		assertEquals(3,ItemManager.instance().numItemsInProductAndContainer(_cpg2_1, _p));
		
		assertFalse(_pg1_2.containsItem(_pItems[0]));
	}

	@Test
	public void testMoveItemInsideSU() throws IllegalAddException {
		
		// Test moving within storage unit
		ItemManager.instance().moveItem(_pg1_1, _pItems[0]);
		
		assertTrue("Should get item",_pg1_1.containsItem(_pItems[0]));
		assertTrue("Should get item",_pg1_1.containsItem(_pItems[1]));
		assertTrue("Should get item",_pg1_1.containsItem(_pItems[2]));
		assertTrue("Should get product",_pg1_1.containsProduct(_p));
		
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[0]));
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[1]));
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[2]));
		assertFalse("Should have lost product",_pg1_2.containsProduct(_p));
	}
	
	@Test
	public void testMoveItemToSameContainer() throws IllegalAddException {
		
		// Test moving to self
		ItemManager.instance().moveItem(_pg1_2, _pItems[0]);
		
		assertTrue("Should still have item",_pg1_2.containsItem(_pItems[0]));
		assertTrue("Should still have item",_pg1_2.containsItem(_pItems[1]));
		assertTrue("Should still have item",_pg1_2.containsItem(_pItems[2]));
		assertTrue("Should still have product",_pg1_2.containsProduct(_p));
	}	
	
	@Test
	public void testMoveItemBetweenSUWithProduct() throws IllegalAddException {
		
		// Test moving between storage unit
		ItemManager.instance().moveItem(_pg2_2, _pItems[0]);
		
		assertTrue("Should get item",_pg2_2.containsItem(_pItems[0]));
		assertTrue("Should get other items from SU",_pg2_2.containsItem(_pItems[3]));
		assertTrue("Should get other items from SU",_pg2_2.containsItem(_pItems[4]));
		assertTrue("Should get product",_pg2_2.containsProduct(_p));
		
		assertFalse("Should have lost item",_pg1_2.containsItem(_pItems[0]));
		assertTrue("Should not lose other items",_pg1_2.containsItem(_pItems[1]));
		assertTrue("Should not lose other items",_pg1_2.containsItem(_pItems[2]));
		assertTrue("Should not lose product",_pg1_2.containsProduct(_p));
		
		assertFalse("Old container in SU should not have item",_cpg2_1.containsItem(_pItems[3]));
		assertFalse("Old container in SU should not have item",_cpg2_1.containsItem(_pItems[4]));
		assertFalse("Old container in SU should not have product",_cpg2_1.containsProduct(_p));
	}	
	
	@Test
	public void testMoveItemBetweenSUWithoutProduct() throws IllegalAddException {
		ItemManager.instance().moveItem(_pg4_1, _pItems[0]);
		
		assertTrue("Should get item",_pg4_1.containsItem(_pItems[0]));
		assertTrue("Should get product",_pg4_1.containsProduct(_p));
		assertEquals("Should only end up with on item",1,
				ItemManager.instance().numItemsInProductAndContainer(_pg4_1, _p));

		
		assertFalse("Should lose item",_pg1_2.containsItem(_pItems[0]));
		assertTrue("Should not lose other item",_pg1_2.containsItem(_pItems[1]));
		assertTrue("Should not lose other item",_pg1_2.containsItem(_pItems[2]));
		assertTrue("Should not lose product",_pg1_2.containsProduct(_p));
	}	

	@Test
	public void testRemoveItem() throws IllegalRemoveException {
		ItemManager.instance().removeItem(_pItems[0]);
		assertTrue(_pItems[0].isUsed());
	}

	@Test
	public void testGetItems() {
		assertNull(_root.getItems());
		
		assertNull(_su1.getItems());
		assertNull(_pg1_1.getItems());
		assertNotNull(_pg1_2.getItems());
		assertTrue(_pg1_2.getItems().hasNext());
		assertFalse(_cpg1_2.getItems().hasNext());
		assertFalse(_ccpg1_2.getItems().hasNext());
		
		assertNull(_su2.getItems());
		assertNull(_pg2_1.getItems());
		assertNull(_pg2_2.getItems());
		assertNotNull(_cpg2_1.getItems());
		assertTrue(_cpg2_1.getItems().hasNext());
		
		assertNotNull(_su3.getItems());
		assertTrue(_su3.getItems().hasNext());
		assertNull(_pg3_1.getItems());
		
		assertNull(_su4.getItems());
		assertFalse(_pg4_1.getItems().hasNext());
		assertNull(_cpg4_1.getItems());
		
		assertNull(_su5.getItems());
	}

	@Test
	public void testGetItemsProduct() throws IllegalAddException {
		Item extraItem = ItemManager.instance().createItem(_p2, _pg1_2);
		ItemManager.instance().addItemToContainer(_pg1_2, extraItem);
		
		TreeSet<Item> expected = new TreeSet<Item>();
		expected.add(_pItems[0]);
		expected.add(_pItems[1]);
		expected.add(_pItems[2]);
		
		Iterator<Item> iter = _pg1_2.getItems(_p);
		while(iter.hasNext()){
			Item i = iter.next();
			assertTrue(expected.contains(i));
			expected.remove(i);
		}
		assertTrue(expected.isEmpty());
		
		assertNull(_su4.getItems(_p));
		assertFalse(_cpg1_2.getItems(_p).hasNext());
		
		
	}

	@Test
	public void testContainsItem() {
		assertTrue(_pg1_2.containsItem(_pItems[0]));
		assertTrue(_pg1_2.containsItem(_pItems[1]));
		assertTrue(_pg1_2.containsItem(_pItems[2]));
		
		assertTrue(_cpg2_1.containsItem(_pItems[3]));
		assertTrue(_cpg2_1.containsItem(_pItems[4]));
		
		assertTrue(_su3.containsItem(_pItems[5]));
		
		assertFalse(_pg1_1.containsItem(_pItems[0]));
		assertFalse(_su1.containsItem(_pItems[0]));
		assertFalse(_cpg1_2.containsItem(_pItems[0]));
		assertFalse(_ccpg1_2.containsItem(_pItems[0]));
		assertFalse(_su2.containsItem(_pItems[0]));
		assertFalse(_su3.containsItem(_pItems[0]));
		
		assertFalse(_pg1_2.containsItem(_pItems[3]));
		assertFalse(_pg1_2.containsItem(null));
	}
	
	@Test
	public void testCompareContainers() throws IllegalAddException{
		Container t = _su5.addContainer("SU1");
		assertTrue(!t.equals(_su1));
		assertTrue(t.compareTo(_su1) > 0);
		assertTrue(_su1.compareTo(t) < 0);
		assertTrue(_su2.compareTo(t) > 0);
		assertTrue(_su2.compareTo(_su1) > 0);
		
		Container x = _su5.addContainer("SU5");
		assertTrue(x.compareTo(_su5) != 0);
		assertTrue(_su5.compareTo(x) != 0);
		
		Container y = _root.addContainer("RootStorageUnit");
		assertTrue(_root.compareTo(y) != 0);
	}
	
	@Test
	public void testStorageUnitContainerForProduct(){
		assertEquals(_su1._storageUnit.getContainerForProduct(_p),_pg1_2);
		assertEquals(_su2._storageUnit.getContainerForProduct(_p),_cpg2_1);
		assertEquals(_su3._storageUnit.getContainerForProduct(_p),_su3);
		assertNull(_su4._storageUnit.getContainerForProduct(_p));
		assertNull(_su5._storageUnit.getContainerForProduct(_p));
	}
	
	@Test
	public void testStorageUnitHasDescendant(){
		assertTrue(_su1._storageUnit.hasDescendant(_su1));
		assertTrue(_su1._storageUnit.hasDescendant(_pg1_1));
		assertTrue(_su1._storageUnit.hasDescendant(_pg1_2));
		assertTrue(_su1._storageUnit.hasDescendant(_cpg1_2));
		assertTrue(_su1._storageUnit.hasDescendant(_ccpg1_2));
		
		assertFalse(_su1._storageUnit.hasDescendant(null));
		assertFalse(_su1._storageUnit.hasDescendant(_su3));
	}

}
